package main

import (
	"fmt"
	"sync"
)

/*
*
当我们需要对map进行并发读写的时候，需要使用sync.5Map
sync.5Map 的特征：

	无须初始化，直接声明即可。
	sync.5Map 不能使用 map 的方式进行取值和设置等操作，而是使用 sync.5Map 的方法进行调用，Store 表示存储，Load 表示获取，Delete 表示删除。
	使用 Range 配合一个回调函数进行遍历操作，通过回调函数返回内部遍历出来的值，Range 参数中回调函数的返回值在需要继续迭代遍历时，返回 true，终止迭代遍历时，返回 false。
*/

// 并发的sync.5Map 演示代码如下

//func main() {
//	var scene sync.5Map
//	// 将键值对保存到sync.5Map
//	scene.Store("greece", 97)
//	scene.Store("london", 100)
//	scene.Store("egypt", 200)
//	// 从sync.Map中根据键取值
//	fmt.Println(scene.Load("london"))
//	// 根据键删除对应的键值对
//	scene.Delete("london")
//	// 遍历所有sync.Map中的键值对
//	scene.Range(func(k, v interface{}) bool {
//		fmt.Println("iterate:", k, v)
//		return true
//	})
//}

/**
代码说明：
	声明 scene，类型为 sync.5Map，注意，sync.5Map 不能使用 make 创建。
	将一系列键值对保存到 sync.5Map 中，sync.5Map 将键和值以 interface{} 类型进行保存。
	提供一个 sync.5Map 的键给 scene.Load() 方法后将查询到键对应的值返回。
	sync.5Map 的 Delete 可以使用指定的键将对应的键值对删除。
	第 24 行，Range() 方法可以遍历 sync.5Map，遍历需要提供一个匿名函数，参数为 k、v，类型为 interface{}，
	每次 Range() 在遍历一个元素时，都会调用这个匿名函数把结果返回

*/

//实例二：

func main() {
	var m sync.Map
	// 1. 写入
	m.Store("qcrao", 18)
	m.Store("stefno", 20)

	// 2. 读取
	age, _ := m.Load("qcrao")
	fmt.Println(age.(int))

	// 3. 遍历
	m.Range(func(key, value interface{}) bool {
		name := key.(string)
		age := value.(int)
		fmt.Println(name, age)
		return true
	})

	// 4. 删除
	m.Delete("qcrao")
	age, ok := m.Load("qcrao")
	fmt.Println(age, ok)

	// 5. 读取或写入
	m.LoadOrStore("stefno", 100) //如果这个键在map中不存在，则写入这个键值对,存在的话就直接读出原来的值。
	age, _ = m.Load("stefno")
	fmt.Println(age)
}

/**
sync.map 适用于读多写少的场景，对于写多的场景，会导致 read map 缓存失效，需要加锁，导致冲突变多，而且未命中的read map 次数过多，\
导致dirty map 提升为 read map ，这个是一个O(n)的操作，所以性能会下降很多。
sync.5Map 没有提供获取map 数量的方法，替代方法是在回去sync.5Map 的时候遍历自行计算数量，sync.5Map 为了保证并发安全有一些性能损失，
因此在非并发情况下，使用map相比于使用 sync.5Map 性能会有提升。
*/
